package org.softmed.rest.server.defaults.generation;

import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;

import org.softmed.reflection.utils.FieldInfo;
import org.softmed.reflection.utils.ReflectionUtil;
import org.softmed.rest.generation.scafold.AppConfig;
import org.softmed.rest.generation.scafold.ModConfig;
import org.softmed.rest.generation.scafold.build.ApplicationProcessor;

public class DefaultPersistenceValidator implements ApplicationProcessor {

	@Override
	public void process(List<AppConfig> apps) throws Throwable {
		for (AppConfig appConfig : apps) {
			validate(appConfig);
		}
	}

	private void validate(AppConfig appConfig) throws Throwable {
		if (appConfig.getClasses() == null
				|| appConfig.getClasses().size() == 0)
			return;

		String persistenceProvider = appConfig.getPersistenceProvider();
		List<ModConfig> mods = appConfig.getModules();
		for (ModConfig modConfig : mods) {

			if (modConfig.getClasses() == null
					|| modConfig.getClasses().size() == 0)
				continue;

			String modPersistenceProvider = modConfig.getPersistenceProvider();
			if (persistenceProvider == null && modPersistenceProvider == null) {
				write("Module "
						+ modConfig.getName()
						+ " has no specific persistence provider and Application "
						+ appConfig.getName()
						+ " has no fallback persistence provider !!!");
			}

			if (modPersistenceProvider != null) {
				// if module has a specific persistence provider
				// its stored classes cannot reference any other classes
				// than the ones registered with the module
				validateModuleOnlyClasses(appConfig, modConfig);

			}
		}
	}

	private void validateModuleOnlyClasses(AppConfig appConfig,
			ModConfig modConfig) throws Throwable {
		ReflectionUtil util = new ReflectionUtil();
		List<Class> classes = modConfig.getClasses();
		for (Class type : classes) {

			List<FieldInfo> fieldInfos = util.getFieldInfos(type);
			for (FieldInfo fieldInfo : fieldInfos) {
				if (util.isPrimitiveOrProxyOrString(fieldInfo.getType()))
					continue;

				if (List.class.isAssignableFrom(fieldInfo.getType())) {
					Field field = util.getField(type, fieldInfo.getName());
					Class listType = util.isSpecificList(field);

					if (listType == null) {
						write("Class " + type.getCanonicalName()
								+ " has a non-generic list field named "
								+ fieldInfo.getName() + " in Module "
								+ modConfig.getName() + " of Application "
								+ appConfig.getName() + " ");
						continue;
					}

					if (util.isPrimitiveOrProxyOrString(listType))
						continue;

					// if (!classes.contains(listType)) {
					if (!containsClass(classes, listType)) {
						write("Class "
								+ type.getCanonicalName()
								+ " of module "
								+ modConfig.getName()
								+ " has a field named "
								+ fieldInfo.getName()
								+ " which is a generic list of the class "
								+ listType.getCanonicalName()
								+ " not registered in the same Module of application "
								+ appConfig.getName() + " ");
						showTip(appConfig, modConfig);
						continue;
					}
					// System.out.println("list field");

					continue;
				}

				// if (!classes.contains(fieldInfo.getType())) {
				if (!containsClass(classes, fieldInfo.getType())) {
					write("Class "
							+ type.getCanonicalName()
							+ " of module "
							+ modConfig.getName()
							+ " has a field named "
							+ fieldInfo.getName()
							+ " which is of the class "
							+ fieldInfo.getType().getCanonicalName()
							+ " not registered in the same Module of application "
							+ appConfig.getName() + " ");
					showTip(appConfig, modConfig);
					continue;
				}
			}

		}
	}

	protected boolean containsClass(List<Class> classes, Class target) {
		for (Class type : classes) {
			if (type.getCanonicalName().equals(target.getCanonicalName()))
				return true;
		}
		return false;
	}

	private void showTip(AppConfig appConfig, ModConfig modConfig) {
		write("The Module "
				+ modConfig.getName()
				+ " has a specific persistence provider, so the classes registered in this module cannot reference classes outside the module, even if in the same Application "
				+ appConfig.getName());
	}

	public void write(String msg) {
		System.err.println(msg);
	}

}
